import 'dart:async';
import 'dart:math';

import 'package:canvas_clock/clock.dart';
import 'package:canvas_clock/main.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_clock_helper/model.dart';

const spinUpDuration = Duration(milliseconds: 1735);

class Clock extends StatefulWidget {
  final ClockModel model;

  /// Defines the clock's color palette.
  ///
  /// See [Palette].
  final Map<ClockColor, Color> palette;

  const Clock({
    Key? key,
    required this.model,
    required this.palette,
  }) : super(key: key);

  @override
  State createState() => _ClockState();
}

class _ClockState extends State<Clock> with TickerProviderStateMixin {
  late ClockModel model;

  Timer? updateTimer;

  late final AnimationController analogBounceController;
  late final AnimationController backgroundWaveController;
  late final AnimationController ballTravelController;
  late final AnimationController ballArrivalController;
  late final AnimationController ballDepartureController;
  late final AnimationController bounceController;
  late final AnimationController minuteController;
  late final AnimationController spinUpController;

  late final BallTrips ballTrips;

  double minuteProgress(DateTime time) =>
      (time.second + time.millisecond / 1e3 + time.microsecond / 1e6) / 60;

  @override
  void initState() {
    super.initState();

    model = widget.model;

    final time = DateTime.now();

    spinUpController = AnimationController(
      vsync: this,
      duration: spinUpDuration,
    )..forward();

    analogBounceController = AnimationController(
      vsync: this,
      duration: handBounceDuration,
      value: 1,
    );

    backgroundWaveController = AnimationController(
      vsync: this,
      duration: waveDuration,
    )..forward(from: waveProgress(time));

    ballTrips = BallTrips();
    ballTravelController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: ballEvery) -
          departureDuration -
          arrivalDuration,
    )..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          ballTravelController.reset();
          ballArrivalController.forward(from: 0);
        }
      });

    ballArrivalController = AnimationController(
      vsync: this,
      duration: arrivalDuration,
    )..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          ballArrivalController.reset();
          ballDepartureController.forward(from: 0);
          Palette.of(context)?.vibrant = !Palette.of(context)!.vibrant;
          bounceController.forward(from: 0);
        }
      });

    ballDepartureController = AnimationController(
      vsync: this,
      duration: departureDuration,
    )..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          ballDepartureController.reset();
          ballTrips.count++;
          ballTravelController.forward(
              from: ballTravelProgress(DateTime.now()));
        }
      });

    bounceController = AnimationController(
      vsync: this,
      duration: bounceAwayDuration + bounceBackDuration,
    );

    minuteController = AnimationController(
      vsync: this,
      duration: const Duration(minutes: 1),
    )..forward(from: minuteProgress(time));

    widget.model.addListener(modelChanged);

    update(true);
  }

  @override
  void dispose() {
    updateTimer?.cancel();
    analogBounceController.dispose();
    backgroundWaveController.dispose();
    ballTravelController.dispose();
    ballArrivalController.dispose();
    ballDepartureController.dispose();
    bounceController.dispose();
    minuteController.dispose();
    spinUpController.dispose();
    super.dispose();
  }

  @override
  void didUpdateWidget(Clock oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.model == widget.model) return;
    oldWidget.model.removeListener(modelChanged);
    widget.model.addListener(modelChanged);
  }

  void modelChanged() {
    setState(() {
      model = widget.model;
    });
  }

  double ballTravelProgress(DateTime time) {
    final toGo = ballEvery * 1e6 ~/ 1 -
        (time.second % ballEvery) * 1e6 ~/ 1 -
        time.microsecond -
        time.millisecond * 1e3 ~/ 1 -
        arrivalDuration.inMicroseconds;

    return max(0, 1 - toGo / ballTravelController.duration!.inMicroseconds);
  }

  void update([bool initial = false]) {
    final time = DateTime.now();

    // 优化Timer调度，使用固定的更新间隔
    if (updateTimer?.isActive ?? false) return;
    
    updateTimer = Timer(
        Duration(
            microseconds:
                1e6 ~/ 1 - time.microsecond - time.millisecond * 1e3 ~/ 1),
        update);

    // 只在必要时更新动画状态
    if (!ballArrivalController.isAnimating &&
        !ballDepartureController.isAnimating &&
        !ballTravelController.isAnimating) {
      ballTravelController.forward(from: ballTravelProgress(time));
    }

    if (initial) return;

    // 避免不必要的动画重置
    if (!minuteController.isAnimating) {
      minuteController.forward(from: minuteProgress(time));
    }
    if (!analogBounceController.isAnimating) {
      analogBounceController.forward(from: 0);
    }

    // 优化波浪动画的状态更新
    if (!backgroundWaveController.isAnimating) {
      final progress = waveProgress(time);
      if ((backgroundWaveController.status == AnimationStatus.reverse ||
              (time.second == 0 && backgroundWaveController.value > 1 / 2)) &&
          !(time.second == 0 && backgroundWaveController.value < 1 / 2)) {
        backgroundWaveController.reverse(from: 1 - progress);
      } else {
        backgroundWaveController.forward(from: progress);
      }
    }
  }

  Animation<double> get analogBounceAnimation => analogBounceController;

  Animation<double> get backgroundWaveAnimation {
    return CurvedAnimation(
      parent: backgroundWaveController,
      curve: waveCurve,
      reverseCurve: waveCurve.flipped,
    );
  }

  Animation<double> get ballTravelAnimation {
    return CurvedAnimation(
      parent: ballTravelController,
      curve: travelCurve,
    );
  }

  Animation<double> get ballArrivalAnimation {
    return CurvedAnimation(
      parent: ballArrivalController,
      curve: arrivalCurve,
    );
  }

  Animation<double> get ballDepartureAnimation {
    return CurvedAnimation(
      parent: ballDepartureController,
      curve: departureCurve,
    );
  }

  Animation<double> get bounceAnimation {
    return TweenSequence([
      TweenSequenceItem(
        tween: Tween<double>(begin: 0, end: 1)
            .chain(CurveTween(curve: bounceAwayCurve)),
        weight: bounceAwayDuration.inMicroseconds /
            bounceController.duration!.inMicroseconds,
      ),
      TweenSequenceItem(
        tween: Tween<double>(begin: 1, end: 0)
            .chain(CurveTween(curve: bounceBackCurve)),
        weight: bounceBackDuration.inMicroseconds /
            bounceController.duration!.inMicroseconds,
      ),
    ]).animate(bounceController);
  }

  Animation<double> get minuteAnimation => minuteController;

  Animation<double> get spinUpAnimation {
    return CurvedAnimation(
      parent: spinUpController,
      curve: Curves.easeIn,
    );
  }

  @override
  Widget build(BuildContext context) {
    return CompositedClock(
      spinUpAnimation: spinUpAnimation,
      children: <Widget>[
        AnimatedAnalogTime(
          animation: analogBounceAnimation,
          bounceAnimation: bounceAnimation,
          model: model,
          palette: widget.palette,
        ),
        AnimatedDigitalTime(
          animation: minuteAnimation,
          model: model,
          palette: widget.palette,
        ),
        AnimatedTemperature(model: model, palette: widget.palette),
        AnimatedWeather(model: model, palette: widget.palette),
        Background(
          animation: backgroundWaveAnimation,
          analogTimeBounceAnimation: bounceAnimation,
          ballColor: Color.lerp(
                widget.palette[ClockColor.ballPrimary] ?? Colors.transparent,
                widget.palette[ClockColor.ballSecondary] ?? Colors.transparent,
                1 / 2,
              ) ??
              Colors.transparent,
          groundColor:
              widget.palette[ClockColor.background] ?? Colors.transparent,
          gooColor: widget.palette[ClockColor.goo] ?? Colors.transparent,
          analogTimeComponentColor:
              widget.palette[ClockColor.analogTimeBackground] ??
                  Colors.transparent,
          temperatureComponentColor: Color.lerp(
                widget.palette[ClockColor.thermometerBackgroundPrimary] ??
                    Colors.transparent,
                widget.palette[ClockColor.thermometerBackgroundSecondary] ??
                    Colors.transparent,
                1 / 2,
              ) ??
              Colors.transparent,
          weatherComponentColor: widget.palette[ClockColor.weatherBackground] ??
              Colors.transparent,
        ),
        Ball(
          travelAnimation: ballTravelAnimation,
          arrivalAnimation: ballArrivalAnimation,
          departureAnimation: ballDepartureAnimation,
          trips: ballTrips,
          primaryColor:
              widget.palette[ClockColor.ballPrimary] ?? Colors.transparent,
          secondaryColor:
              widget.palette[ClockColor.ballSecondary] ?? Colors.transparent,
          dotsIdleColor:
              widget.palette[ClockColor.dotsIdleColor] ?? Colors.transparent,
          dotsPrimedColor:
              widget.palette[ClockColor.dotsPrimedColor] ?? Colors.transparent,
          dotsDisengagedColor: widget.palette[ClockColor.dotsDisengagedColor] ??
              Colors.transparent,
          shadowColor: widget.palette[ClockColor.shadow] ?? Colors.transparent,
        ),
        Location(
          text: model.location,
          textStyle: TextStyle(
            color: widget.palette[ClockColor.text] ?? Colors.transparent,
            fontWeight: FontWeight.bold,
          ),
          shadowColor: widget.palette[ClockColor.shadow] ?? Colors.transparent,
        ),
        Slide(
          ballTravelAnimation: ballTravelAnimation,
          ballArrivalAnimation: ballArrivalAnimation,
          ballDepartureAnimation: ballDepartureAnimation,
          primaryColor:
              widget.palette[ClockColor.slidePrimary] ?? Colors.transparent,
          secondaryColor:
              widget.palette[ClockColor.slideSecondary] ?? Colors.transparent,
          shadowColor: widget.palette[ClockColor.shadow] ?? Colors.transparent,
          spinUpAnimation: spinUpAnimation,
        ),
        UpdatedDate(palette: widget.palette),
      ],
    );
  }
}
